#include "StdAfx.h"
#include "ColladaFile.h"
#include "Utility\FileManager.h"
#include "ColladaGeometry.h"

static const u32 s_uHash_library_images =		0x5a2a2cef;

static const u32 s_uHash_image =				0xfa87ca8;
static const u32 s_uHash_convex_mesh =			0xb0431924;
static const u32 s_uHash_spline =				0x1c4803b0;
static const u32 s_uHash_vertices =				0xd31fda6a;
static const u32 s_uHash_triangles =			0x6cb8b6ce;
static const u32 s_uHash_polygons =				0x3db5dee0;
static const u32 s_uHash_POSITION =				0xd87309ba;
static const u32 s_uHash_NORMAL =				0xc3953cee;
static const u32 s_uHash_VERTEX =				0xd589ab43;
static const u32 s_uHash_TEXCOORD =				0x92ee91cd;
static const u32 s_uHash_extra =				0x0f667509;
static const u32 s_uHash_translate =			0x2396ec73;
static const u32 s_uHash_rotate =				0x19e50454;
static const u32 s_uHash_instance_geometry =	0x1c78d0c5;
static const u32 s_uHash_rotateX =				0x56858f2c;
static const u32 s_uHash_rotateY =				0x56858f2d;
static const u32 s_uHash_rotateZ =				0x56858f2e;
static const u32 s_uHash_source =				0x1c3aff76;

static const u32 s_uHash_controller =			0xbf67c809;
static const u32 s_uHash_library_controllers =	0xcdb8c8f0;
static const u32 s_uHash_input	=				0x0fa94ab5;
static const u32 s_uHash_library_visual_scenes =0x22ad11cd;
static const u32 s_uHash_library_geometries =	0x9a15dacd;
static const u32 s_uHash_node =					0x7c9b46ab;
static const u32 s_uHash_visual_scene =			0x8deb6446;
static const u32 s_uHash_JOINT	=				0xd66a849;
static const u32 s_uHash_NODE =					0x7c892e2b;
static const u32 s_uHash_matrix	=				0xd3e291a;
static const u32 s_uHash_INV_BIND_MATRIX = 		0x1be47042;
static const u32 s_uHash_joints	=				0x735ee7c;
static const u32 s_uHash_geometry =				0xf3c45451;
static const u32 s_uHash_mesh =					0x7c9a91b2;
static const u32 s_uHash_library_materials =    0xac3922db;
static const u32 s_uHash_library_effects =		0x5b8c1539;
static const u32 s_uHash_material =				0x54865594;
static const u32 s_uHash_instance_effect =		0xe7aa48a6;
static const u32 s_uHash_effect =				0xfae77932;
static const u32 s_uHash_surface =				0xb15cfb8e;

static const u32 s_uHash_vertex_weights =		0x8160d17d;
static const u32 s_uHash_vcount =				0x225e1744;
static const u32 s_uHash_v =					0x2b61b;

static const u32 s_uHash_skin =					0x7c9df43a;
static const u32 s_uHash_WEIGHT =				0xd7d9ad8d;
static const u32 s_uHash_instance_controller =  0xe72d317d;

static const u32 s_uHash_library_animations =	0x751f88c;
static const u32 s_uHash_animation =			0x318ebea5;
static const u32 s_uHash_OUTPUT =				0xc65820b6;
static const u32 s_uHash_INPUT =				0xd542215;
static const u32 s_uHash_sampler =				0x8260ff59;

static const u32 s_uHash_channel =				0xc237195e;
static const u32 s_uHash_transform =			0x2393dd81;


ColladaFile::ColladaFile(void)
{
	m_plControllers = 0;
	m_paBones = 0;

	m_plSkeletonSources = 0;	
	m_pBoneIndiceMap = 0;		
	m_plBoneHierarchy = 0;
	m_paInvBinds = 0;
}

ColladaFile::~ColladaFile(void)
{
	for(int i = 0; i < m_aGeometries.GetNumElements(); i++)
	{
		if( m_aGeometries[i] )
			delete m_aGeometries[i];
	}

	if( m_pBoneIndiceMap )
		delete m_pBoneIndiceMap;

	if(	m_plSkeletonSources )
	{
		LinkedList* pNext = m_plSkeletonSources->Next();
		while( pNext )
		{
			delete pNext->GetPointer();
			delete pNext;
			pNext = m_plSkeletonSources->Next();
		}
	
		delete m_plSkeletonSources;
		m_plSkeletonSources = 0;
	}

	
	if( m_plBoneHierarchy )
	{
		LinkedList* pNext = m_plBoneHierarchy->Next();
		while( pNext )
		{
			delete pNext->GetPointer();
			delete pNext;
			pNext = m_plBoneHierarchy->Next();
		}		
		delete m_plBoneHierarchy;
		m_plBoneHierarchy = 0;
	}	

	if( m_paInvBinds )
		delete m_paInvBinds;

	if( m_paBones )
		delete m_paBones;
}

bool ColladaFile::Load(const char* cszFileName)
{
	bool bRet = false;
	// Load the level list
	char szFileName[512];
	FileManager::GetFullFilePath(cszFileName, szFileName, sizeof(szFileName));

	//Set root directory to model directory
	char szOldRoot[512];
	memcpy(szOldRoot, FileManager::GetRoot(), sizeof(szOldRoot));
	char* pszRoot = FileManager::GetDirectory(szFileName);
	FileManager::SetRootPath(pszRoot);	

	m_plControllers = new LinkedList();	

	XmlParser* pParser = new XmlParser();
	if( pParser->LoadFile(szFileName) )
	{
		// Find the COLLADA tag
		for( ;pParser->HasElement(); pParser->NextSiblingElement())
		{
			if( !_strnicmp(pParser->GetValue(), "COLLADA", 7) )
			{
				bRet = true;

				// Parse libraries
				pParser->FirstChildElement();
				while( pParser->HasElement() )					
				{
					switch( (u32)pParser->GetValueAsInt() )
					{
						case s_uHash_library_geometries:
							LoadGeometryLibrary(pParser);
							break;
						case s_uHash_library_controllers:
							LoadLibraryControllers(pParser);
							break;
						case s_uHash_library_animations:
							//load animations
							LoadAnimationLibrary(pParser);
							break;
						case s_uHash_library_visual_scenes:
							//get skeleton tree
							LoadVisualScene(pParser);
							break;
						case s_uHash_library_materials:
							LoadMaterials(pParser);
							break;
						case s_uHash_library_effects:
							LoadEffects(pParser);
							break;
						case s_uHash_library_images:
							LoadImages(pParser);
							break;
						default:
							break;
					}

					pParser->NextSiblingElement();
				}

				pParser->Parent();
			}
		}
	} 	

	//reset root directory
	FileManager::SetRootPath(szOldRoot);
	free(pszRoot);

	//clear out controllers
	LinkedList* pNext = m_plControllers->Next();
	while( pNext )
	{
		sController* pController = (sController*)pNext->GetPointer();
		//_aligned_free(pController->aInverseBinds);
		//_aligned_free(pController->aVertexWeights);
		delete pController;
		delete pNext;
		pNext = m_plControllers->Next();
	}

	delete m_plControllers;
	m_plControllers = 0;

	delete pParser;

	return bRet;
}

void ColladaFile::LoadLibraryControllers(XmlParser *pParser)
{
	LinkedList		sources;
	char*			pVertexInfluenceCount = 0;
	sSource*		pWeightSource = 0;	

	m_plSkeletonSources = new LinkedList();

	pParser->FirstChildElement();

	while( pParser->HasElement() )
	{
		//make sure it is a controler
		if( pParser->GetValueAsInt() == s_uHash_controller )
		{
			sController* pController = new sController();
			pController->uID = pParser->GetAttributeAsInt("id");

			pParser->FirstChildElement();

			if( pParser->GetValueAsInt() == s_uHash_skin )
			{				
				pController->uGeometryID = StringHash(pParser->GetAttribute("source") + 1);
				pParser->FirstChildElement();

				while( pParser->HasElement() )
				{
					switch( (u32)pParser->GetValueAsInt() )
					{
					case s_uHash_source:
						{
							sSource* pSource = pParser->LoadSource();
							sources.AddTail(pSource);
							break;
						}
					case s_uHash_joints:
						//load the skeleton and inverse bind matrices
						pParser->FirstChildElement();
						while( pParser->HasElement() )
						{
							switch( pParser->GetAttributeAsInt("semantic") )
							{
							case s_uHash_JOINT:
								LoadBoneIndices(pParser, &sources, pController);
								break;
							case s_uHash_INV_BIND_MATRIX:
								LoadInverseBinds(pParser, &sources);
								break;
							}
							pParser->NextSiblingElement();
						}
						pParser->Parent();
						break;		
					case s_uHash_vertex_weights:
						{
							pController->iVertexCount = pParser->GetAttributeAsInt("count");
							pController->aBoneIndices.SetSize(pController->iVertexCount);
							pController->aVertexWeights.SetSize(pController->iVertexCount);

							pParser->FirstChildElement();
							while( pParser->HasElement() )
							{
								switch( (u32)pParser->GetValueAsInt() )
								{
								case s_uHash_input:
									//get the semantics
									if( (u32)pParser->GetAttributeAsInt("semantic") == s_uHash_WEIGHT )
									{
										LinkedList* pNext = sources.Next();
										while(pNext)
										{
											sSource* source = (sSource*)pNext->GetPointer();
											u32 uAttrib = StringHash(pParser->GetAttribute("source") + 1);
											if( source->uID == uAttrib )
											{	
												source->eSemantic = eVS_Weight;	
												pWeightSource = source;
												break;
											}

											pNext = pNext->Next();
										}
									}
									break;
								case s_uHash_vcount:									
									//get the number of influences per vert
									pVertexInfluenceCount = (char*)pParser->GetChildValue();									
									break;
								case s_uHash_v:
									LoadBoneIndicesWeights(pController, pWeightSource, pParser, pVertexInfluenceCount);								
									break;
								}

								pParser->NextSiblingElement();
							}
							pParser->Parent();
							
							break;
						}
					}

					pParser->NextSiblingElement();
				}

				pParser->Parent();
			}
			pParser->Parent();

			m_plControllers->AddTail(pController);
		}
		pParser->NextSiblingElement();
	}
	pParser->Parent();

	//clean up sources
	LinkedList* pNext = sources.Next();
	while( pNext )
	{
		delete pNext;
		pNext = sources.Next();
	}
}

void ColladaFile::LoadBoneIndices(XmlParser* pParser, LinkedList* pSources, sController* pController)
{	
	u32 uSourceID = StringHash(pParser->GetAttribute("source") + 1);

	//find the source that contains the name of the joints
	LinkedList* pNext = pSources->Next();
	while( pNext )
	{
		sSource* source = (sSource*)pNext->GetPointer();
		if( uSourceID == source->uID )
		{
			//found joint array add to controller
			pController->paBones = &source->aHashes;
			m_plSkeletonSources->AddTail(source);
			delete pNext;
			break;
		}
		pNext = pNext->Next();
	}
}

void ColladaFile::LoadInverseBinds(XmlParser* pParser, LinkedList* pSources)
{
	u32 uSourceID = StringHash(pParser->GetAttribute("source") + 1);

	//create array of skinning matricies
	LinkedList* pNext = pSources->Next();
	while(pNext)
	{
		sSource* source = (sSource*)pNext->GetPointer();
		if( source->uID == uSourceID )
		{
			int iCount = source->aFloats.GetNumElements() / 16;

			if( m_paInvBinds )
				delete m_paInvBinds;

			m_paInvBinds = new FixedArray<Matrix>(iCount);
			//found the bone matricies
			for(int i = 0; i < source->aFloats.GetNumElements(); i+= 16)
			{
				Vector4 v(source->aFloats[i], source->aFloats[i + 1], source->aFloats[i + 2], source->aFloats[i + 3]);
				Vector4 v1(source->aFloats[i + 4], source->aFloats[i + 5], source->aFloats[i + 6], source->aFloats[i + 7]);
				Vector4 v2(source->aFloats[i + 8], source->aFloats[i + 9], source->aFloats[i + 10], source->aFloats[i + 11]);
				Vector4 v3(source->aFloats[i + 12], source->aFloats[i + 13], source->aFloats[i + 14], source->aFloats[i + 15]);

				Matrix mMatrix(v,v1,v2,v3);
				//transpose for cpu
				mMatrix.Transpose();
				m_paInvBinds->Add(mMatrix);
			}

			//delete this node its usefullness is gone
			delete source;
			LinkedList* pTemp = pNext->Next();
			delete pNext;
			break;

			pNext = pTemp;
		}	
		pNext = pNext->Next();
	}
}

void ColladaFile::LoadBoneIndicesWeights(sController* pController, sSource* pWeightSource, XmlParser* pParser, const char* pszIndexCounts)
{
	//get the bones indicies
	char* pszArray = _strdup((char*)pParser->GetChildValue());
	char* pDataPtr = pszArray;
	char* pCurrentVert = (char*)pszIndexCounts;
	float fWeights[4];
	u32 uBoneIndicies[4];
	for(int i = 0; i < pController->aBoneIndices.GetNumElements(); i++ )
	{
		//get the count for this vertex
		int iCount = atoi(pCurrentVert);
		while( *pCurrentVert && *pCurrentVert != ' ' ) pCurrentVert++;
		pCurrentVert++;

		fWeights[0] = fWeights[1] = fWeights[2] = fWeights[3] = 0.0f;
		uBoneIndicies[0] = uBoneIndicies[1] = uBoneIndicies[2] = uBoneIndicies[3] = 0;
		
		for( int j = 0; j < iCount; j++ )
		{
			//Get bone index
			char* pStart = pDataPtr;
			while( *pDataPtr && *pDataPtr != ' ' ) pDataPtr++;
			*pDataPtr = 0;

			//make index zero based
			uBoneIndicies[j] = atoi(pStart);

			pDataPtr++;			
			
			pStart = pDataPtr;
			while( *pDataPtr && *pDataPtr != ' ' ) pDataPtr++;
			*pDataPtr = 0;

			//make zero based
			int iWeightIndex = atoi(pStart);
			fWeights[j] = pWeightSource->aFloats[iWeightIndex];			

			pDataPtr++;			
		}

		pController->aBoneIndices.Add( (uBoneIndicies[3] << 24) | (uBoneIndicies[2] << 16) | (uBoneIndicies[1] << 8) | uBoneIndicies[0] );
		pController->aVertexWeights[i] = Vector4(fWeights[0], fWeights[1], fWeights[2], fWeights[3]);		
	}
	free(pszArray);
}

ColladaGeometry* ColladaFile::LoadMesh(XmlParser* pParser)
{
	ColladaGeometry* pGeometry = 0;
	LinkedList sources;
	u16* pIndexArray = 0;
	int iIndexCount;

	pParser->FirstChildElement();
	while( pParser->HasElement() )
	{
		switch( (u32)pParser->GetValueAsInt() )
		{
			case s_uHash_source:
				{
					sSource* pSource = pParser->LoadSource();
					if( pSource )
						sources.AddTail(pSource);
				}
				break;
			case s_uHash_vertices:
				LoadVertices(pParser, sources);
				break;
			case s_uHash_triangles:
				pIndexArray = LoadTriangles(pParser, sources, iIndexCount);				
				break;
		}
		pParser->NextSiblingElement();
	}

	pParser->Parent();

	if( pIndexArray )
	{
		// Build the mesh
		sSource* pPositions;
		sSource* pNormals;
		sSource* pUVs;

		LinkedList* pSNode = sources.Next();
		while( pSNode )
		{
			sSource* pSource = (sSource*)pSNode->GetPointer();
			if( pSource )
			{
				switch( pSource->eSemantic )
				{
					case eVS_Position:
						pPositions = pSource;
						break;
					case eVS_Normal:
						pNormals = pSource;
						break;
					case eVS_TexCoord:
						pUVs = pSource;
						break;
					default:
						break;
				}
			}
			pSNode = pSNode->Next();
		}

		pGeometry = new ColladaGeometry();
		int iVertCount = pPositions->aFloats.GetNumElements() / 3;		

		//Get name and set world matrix
		pParser->Parent();
		pGeometry->SetName(pParser->GetAttribute("id"));

		pParser->FirstChildElement();		

		//Get controller for this geometry
		sController* pGeoController = 0;
		LinkedList*	pNext = m_plControllers->Next();
		while( pNext )
		{
			sController* pController = (sController*)pNext->GetPointer();
			if( pController->uGeometryID == pGeometry->GetHash() )
			{
				pGeoController = pController;				

				//Add bone names and inverse binds
				int iBoneCount = pGeoController->paBones->GetNumElements();
				pGeometry->SetBoneCount(iBoneCount);

				for(int i = 0; i < iBoneCount; i++)
				{
					//pGeometry->SetBone(i, (*pGeoController->paBones)[i]);
					//pGeometry->SetInverseBind(i, pGeoController->aInverseBinds[i]);
				}
				break;
			}
			pNext = pNext->Next();
		}

		//set array sizes
		pGeometry->SetVertCount(iVertCount);	
		pGeometry->SetIndexCount(iIndexCount);
		
		for( int i = 0; i < iVertCount; i++ )
		{
			int iIndex = i * 3;
			Vector4 vPos(pPositions->aFloats[iIndex + 0], pPositions->aFloats[iIndex + 1], pPositions->aFloats[iIndex + 2], 0.0f);
			Vector4 vNrm(pNormals->aFloats[iIndex + 0], pNormals->aFloats[iIndex + 1], pNormals->aFloats[iIndex + 2], 1.0f);
			iIndex = i * 2;
			Vector4 vUV(pUVs->aFloats[iIndex + 0], -pUVs->aFloats[iIndex + 1], 0.0f, 0.0f);	

			pGeometry->SetPosition(i, vPos);
			pGeometry->SetNormal(i, vNrm);
			pGeometry->SetUV(i, vUV);

			if( pGeoController )	
			{				
				pGeometry->SetBlendWeights(i, pGeoController->aVertexWeights[i]);
				pGeometry->SetBlendIndices(i, pGeoController->aBoneIndices[i]);
			}
			
		}
		
		for( int i = 0; i < iIndexCount; i++ )
		{
			pGeometry->SetIndice(i, pIndexArray[i]);
		}
		
		delete[] pIndexArray;
	}

	while( sources.Next() )
	{
		LinkedList* pIter = sources.Next();
		sSource* pSource = (sSource*)pIter->GetPointer();
		delete pSource;
		delete pIter;
	}
	
	return pGeometry;
}

void ColladaFile::LoadVertices(XmlParser* pParser, LinkedList& sources)
{	
	pParser->FirstChildElement();
	while( pParser->HasElement() )
	{
		if( (u32)pParser->GetValueAsInt() == s_uHash_input )
		{
			const char* pszSource = pParser->GetAttribute("source");
			const char* ppSource = pszSource + 1;
			u32 uHash = StringHash(ppSource);
			LinkedList* pSourceNode = sources.Next();
			sSource* pTheSource = 0;
			while( pSourceNode )
			{
				sSource* pSource = (sSource*)pSourceNode->GetPointer();
				if( pSource && pSource->uID == uHash )
				{
					pTheSource = pSource;
					break;
				}
				pSourceNode = pSourceNode->Next();
			}

			if( pTheSource )
			{
				switch( (u32)pParser->GetAttributeAsInt("semantic") )
				{
					case s_uHash_POSITION:
						pTheSource->eSemantic = eVS_Position;
						break;
					case s_uHash_NORMAL:
						pTheSource->eSemantic = eVS_Normal;
						break;
				}
			}
		}	

		pParser->NextSiblingElement();
	}

	pParser->Parent();
}

u16* ColladaFile::LoadTriangles(XmlParser* pParser, LinkedList& sources, int& iOutIndexCount)
{	
	int iVertexOffset = -1;
	int iTexCoordOffset = -1;
	int iNormCoordOffset = -1;
	int iIndicies = 0;
	iOutIndexCount = pParser->GetAttributeAsInt("count") * 3;
	u16* pIndexArray = new u16[iOutIndexCount];
	sSource* pTextureSource;
	sSource* pNormalSource = 0;
	pParser->FirstChildElement();
	while( pParser->HasElement() )
	{
		if( (u32)pParser->GetValueAsInt() == s_uHash_input )
		{
			switch( (u32)pParser->GetAttributeAsInt("semantic") )
			{
				case s_uHash_VERTEX:
					iVertexOffset = pParser->GetAttributeAsInt("offset");
					iIndicies++;
					break;
				case s_uHash_TEXCOORD:
					{
						iTexCoordOffset = pParser->GetAttributeAsInt("offset");
						iIndicies++;
						u32 uTexCoordSourceID = StringHash(pParser->GetAttribute("source") + 1);
						LinkedList* pSourceNode = sources.Next();
						while( pSourceNode )
						{
							sSource* pSource = (sSource*)pSourceNode->GetPointer();
							if( pSource && pSource->uID == uTexCoordSourceID )
							{
								pSource->eSemantic = eVS_TexCoord;
								pTextureSource = pSource;
								break;
							}
							pSourceNode = pSourceNode->Next();
						}
						break;
					}	
				case s_uHash_NORMAL:
					{
						iIndicies++;
						iNormCoordOffset = pParser->GetAttributeAsInt("offset");
						u32 uNormalSourceID = StringHash(pParser->GetAttribute("source") + 1);
						LinkedList* pSourceNode = sources.Next();
						while( pSourceNode )
						{
							sSource* pSource = (sSource*)pSourceNode->GetPointer();
							if( pSource && pSource->uID == uNormalSourceID )
							{
								pSource->eSemantic = eVS_Normal;
								pNormalSource = pSource;
								break;
							}
							pSourceNode = pSourceNode->Next();
						}
					
					break;
					}
			}
		}
		else if( pParser->GetValue()[0] == 'p' && pParser->GetValue()[1] == 0 )
		{
			FixedArray<float> aTemp(pTextureSource->aFloats.GetNumElements());
			memcpy(&aTemp[0], &pTextureSource->aFloats[0], sizeof(float) * aTemp.GetNumElements());

			char* pszIndices = _strdup(pParser->GetChildValue());
			char* pIndexPtr = pszIndices;
			for( int i = 0; i < iOutIndexCount; i++ )
			{
				int indices[3];
				for( int j = 0; j < iIndicies; j++ )
				{
					char* pStart = pIndexPtr;
					while( *pIndexPtr && *pIndexPtr != ' ' ) pIndexPtr++;
					*pIndexPtr = 0;

					indices[j] = atoi(pStart);		
					pIndexPtr++;					
				}

				pIndexArray[i] = (u16)indices[iVertexOffset];

				pTextureSource->aFloats[(indices[iVertexOffset] * 2) + 0] = aTemp[(indices[iTexCoordOffset] * 2) + 0];
				pTextureSource->aFloats[(indices[iVertexOffset] * 2) + 1] = aTemp[(indices[iTexCoordOffset] * 2) + 1];		
			}
			free(pszIndices);
		}
		pParser->NextSiblingElement();
	}

	pParser->Parent();

	return pIndexArray;
}

void ColladaFile::LoadGeometryLibrary(XmlParser* pParser)
{
	LinkedList lGeometries;
	int iGeometryCount = 0;

	pParser->FirstChildElement();
	// Go through all the child elements
	while( pParser->HasElement() )
	{
		// Make sure this is a geometry element
		if( (u32)pParser->GetValueAsInt() == s_uHash_geometry )
		{
			pParser->FirstChildElement();

			while( pParser->HasElement() )
			{
				// Load the mesh data			
				if( (u32)pParser->GetValueAsInt() == s_uHash_mesh )
				{					
					ColladaGeometry* pGeometry = LoadMesh(pParser);
					if( pGeometry )
					{											
						lGeometries.AddTail(pGeometry);
						iGeometryCount++;						
					}			
				}

				pParser->NextSiblingElement();
			}

			pParser->Parent();
		}

		pParser->NextSiblingElement();
	}

	pParser->Parent();

	// Convert linked list into fixed array for speed later
	m_aGeometries.SetSize(iGeometryCount);
	int iIndex = 0;
	while( lGeometries.Next() )
	{
		LinkedList* pIter = lGeometries.Next();
		ColladaGeometry* pGeometry = (ColladaGeometry*)pIter->GetPointer();
		m_aGeometries[iIndex++] = pGeometry;
		delete pIter;
	}

	
}

void ColladaFile::LoadAnimationLibrary(XmlParser *pParser)
{
	bool bSkeletonFound = false;	

	//Create new bone array
	m_paBones = new FixedArray<sBone>(MAX_BONES);

	pParser->FirstChildElement();

	while( pParser->HasElement() )
	{
		//make sure we have an animation
		if( (u32)pParser->GetValueAsInt() == s_uHash_animation )
		{
			LinkedList sources;			
			sSource* pTransforms = 0;
			sSource* pTimes = 0;

			pParser->FirstChildElement();

			while( pParser->HasElement() )
			{
				switch( (u32)pParser->GetValueAsInt() )
				{
				case s_uHash_source:
					{
						sSource* pSource = pParser->LoadSource();
						if( pSource )
							sources.AddTail(pSource);
						break;
					}
				case s_uHash_sampler:
					pParser->FirstChildElement();

					while( pParser->HasElement() )
					{
						if( (u32)pParser->GetValueAsInt() == s_uHash_input )
						{
							switch( (u32)pParser->GetAttributeAsInt("semantic") )
							{
							case s_uHash_INPUT:
								{
									//find the times
									LinkedList* pNext = sources.Next();

									while( pNext )
									{
										sSource* pSource = (sSource*)pNext->GetPointer();
										u32 uAttrib = StringHash(pParser->GetAttribute("source") + 1);

										if( pSource->uID == uAttrib )
										{											
											pTimes = pSource;
											delete pNext;
											break;
										}
										pNext = pNext->Next();
									}
								break;
								}
							case s_uHash_OUTPUT:
								{
									//find the bones
									LinkedList* pNext = sources.Next();

									while( pNext )
									{
										sSource* pSource = (sSource*)pNext->GetPointer();
										u32 uAttrib = StringHash(pParser->GetAttribute("source") + 1);

										if( pSource->uID == uAttrib )
										{
											pTransforms = pSource;
											delete pNext;
											break;
										}
										pNext = pNext->Next();
									}
									break;
								}

							}
						}
						pParser->NextSiblingElement();
					}
					pParser->Parent();				
					break;
				case s_uHash_channel:
					{
						char* pTarget = _strdup(pParser->GetAttribute("target"));
						char* pMatrix = strchr(pTarget, '/');
						*pMatrix = 0;
						pMatrix++;

						u32 uTarget = StringHash(pTarget);
						u32 uMatrix = StringHash(pMatrix);
						bool bValid = false;

						//make sure this is a transform
						if( uMatrix == s_uHash_transform)
						{
							//If we still don't know which skeleton look for the skeleton with this bone
							if( !bSkeletonFound )								
								bSkeletonFound = CreateSkeletonMap(uTarget);

							//if skeleton found make sure this bone is in it
							if( bSkeletonFound )
							{
								sBoneData* pData = (*m_pBoneIndiceMap)[uTarget];
								if( pData )
								{
									bValid = true;
									pTransforms->uID = uTarget;	
								}
							}
						}

						//not a valid bone get rid of it
						if( !bValid )
						{
							delete pTransforms;
							pTransforms = 0;
							delete pTimes;
							pTimes = 0;
						}

						free(pTarget);
						break;
					}
				}
				pParser->NextSiblingElement();
			}

			pParser->Parent();

			//if we have transformations for this bone create it
			if( pTransforms && pTimes )
			{
				//get bone data
				sBoneData* pData = (*m_pBoneIndiceMap)[pTransforms->uID];
				if( pData )
				{
					//Make matrices and clean up duplicates
					sBone pBone;
					pBone.aTransforms.SetSize(pTransforms->aFloats.GetNumElements() / 16);
					pBone.aTimes.SetSize(pTimes->aFloats.GetNumElements());

					for(int i = 0; i < pTransforms->aFloats.GetNumElements(); i+= 16)
					{
						Vector4 v(pTransforms->aFloats[i], pTransforms->aFloats[i + 1], pTransforms->aFloats[i + 2], pTransforms->aFloats[i + 3]);
						Vector4 v1(pTransforms->aFloats[i + 4], pTransforms->aFloats[i + 5], pTransforms->aFloats[i + 6], pTransforms->aFloats[i + 7]);
						Vector4 v2(pTransforms->aFloats[i + 8], pTransforms->aFloats[i + 9], pTransforms->aFloats[i + 10], pTransforms->aFloats[i + 11]);
						Vector4 v3(pTransforms->aFloats[i + 12], pTransforms->aFloats[i + 13], pTransforms->aFloats[i + 14], pTransforms->aFloats[i + 15]);
					
						Matrix mMatrix(v, v1, v2, v3);
						//make matrix column major for cpu
						mMatrix.Transpose();
					
						int iKey = i / 16;						

						//see if this matrix is already contained
						if( iKey == 0 || pBone.aTransforms[pBone.aTransforms.GetNumElementsUsed() - 1] != mMatrix )
						{
							pBone.uName = pTransforms->uID;
							pBone.aTransforms.Add(mMatrix);
							pBone.aTimes.Add(pTimes->aFloats[iKey]);						
						}				

					}					
					(*m_paBones)[pData->iIndex] = pBone;		

					delete pTransforms;
					delete pTimes;
					pTransforms = 0;
					pTimes = 0;
				}
			}
		}

		pParser->NextSiblingElement();
	}
	pParser->Parent();

	//make sure all bones are in the array and clean up bone heirarchy
	LinkedList* pNext = m_plBoneHierarchy->Next();
	while( pNext )
	{
		bool bFound = false;
		sBoneData* pData = (sBoneData*)pNext->GetPointer();
	
		for(int i = 0; i < m_paBones->GetNumElements(); i++)
		{
			if( (*m_paBones)[i].uName == pData->uName )
			{
				bFound = true;				
				break;
			}
		}

		if( !bFound )
		{
			//make sure it is in map
			void* p = (*m_pBoneIndiceMap)[pData->uName];
			if( p )
			{
				sBone pBone;
				pBone.uName = pData->uName;			
				(*m_paBones)[pData->iIndex] = pBone;		
			}
		}

		delete pData;
		delete pNext;

		pNext = m_plBoneHierarchy->Next();
	}

}

void ColladaFile::LoadVisualScene(XmlParser* pParser)
{
	pParser->FirstChildElement();

	if( (u32)pParser->GetValueAsInt() == s_uHash_visual_scene )
	{
		m_plBoneHierarchy = new LinkedList();

		pParser->FirstChildElement();
		while( pParser->HasElement() )
		{			
			//find nodes containing joints and create the skeleton
			LoadNode(pParser);			

			pParser->NextSiblingElement();
		}
		pParser->Parent();
	}
	pParser->Parent();
}

void ColladaFile::LoadNode(XmlParser *pParser)
{
	if( pParser->GetValueAsInt() == s_uHash_node )  
	{
		if( pParser->GetAttributeAsInt("type") == s_uHash_JOINT )
			LoadBone(pParser, 0);
		else if( pParser->GetAttributeAsInt("type") == s_uHash_NODE )
		{
			//look for an materials assigned to ths geometry
			pParser->FirstChildElement();
			while( pParser->HasElement() )
			{
				if( pParser->GetValueAsInt() == s_uHash_instance_geometry )
				{
					const char* szGeometry = pParser->GetAttribute("url");
					u32 uGeometry = StringHash(&szGeometry[1]);

					//find the geometry in the list
					for(int i = 0; i < m_aGeometries.GetNumElements(); i++)
					{
						if( m_aGeometries[i]->GetHash() == uGeometry )
						{
							pParser->FirstChildElement();
							pParser->FirstChildElement();
							pParser->FirstChildElement(); //looking for instance_material node, should be third child

							const char* szMat = pParser->GetAttribute("target");
							u32 uMaterial = StringHash(&szMat[1]);
							
							const char** texture = m_materialTextureMap[uMaterial];
							if( texture )
							{
								char* texturePath = new char[512];

								FileManager::GetFullFilePath(*texture, texturePath, 512);

								if( *texture && *texture != "" )
								{
									m_aGeometries[i]->SetTextureFile(texturePath);
								}
							}
							pParser->Parent();
							pParser->Parent();
							pParser->Parent();

							break;
						}
					}

				}
				
				pParser->NextSiblingElement();
			}
			pParser->Parent();
		}

	}
}

void ColladaFile::LoadBone(XmlParser* pParser, u32 uParent)
{
	//found a bone
	sBoneData* pBone = new sBoneData();
	pBone->uParent = uParent;
	pBone->uName = (u32)pParser->GetAttributeAsInt("name");
	m_plBoneHierarchy->AddTail(pBone);

	pParser->FirstChildElement();
		
	while( pParser->HasElement() )
	{
		//add the bones transformation or its child bones
		if( pParser->GetValueAsInt() == s_uHash_matrix )			
			pBone->pFloats = (char*)pParser->GetChildValue();			
		else
			LoadBone(pParser, pBone->uName);

		pParser->NextSiblingElement();
	}
	pParser->Parent();		

}

bool ColladaFile::CreateSkeletonMap(u32 uBone)
{
	sSource* pSkeletonSource = 0;

	LinkedList* pNext = m_plSkeletonSources->Next();
	while( pNext && !pSkeletonSource )
	{
		sSource* pSource = (sSource*)pNext->GetPointer();

		for(int i =0; i < pSource->aHashes.GetNumElements(); i++)
		{
			if( uBone == pSource->aHashes[i] )
			{				
				pSkeletonSource = pSource;
				break;
			}
		}

		pNext = pNext->Next();
	}

	if( pSkeletonSource )
	{
		if( m_pBoneIndiceMap )
			delete m_pBoneIndiceMap;

		m_pBoneIndiceMap = new Map<u32, sBoneData>();

		//store it in map
		for(int i = 0; i < pSkeletonSource->aHashes.GetNumElements(); i++)
		{
			//add bone hierarchy to map
			LinkedList* pNext = m_plBoneHierarchy->Next();
			while( pNext )
			{
				//make sure the bone exist in the map
				sBoneData* pData = (sBoneData*)pNext->GetPointer();

				if( pData->uName == pSkeletonSource->aHashes[i] )	
				{
					//Set the world matrix for each bone
					char* pMatrix = _strdup(pData->pFloats);
					pData->mWorld = ParseMatrix(pMatrix);
					pData->mWorld.Transpose();
					free(pMatrix);

					pData->iIndex = i;

					m_pBoneIndiceMap->Add(pSkeletonSource->aHashes[i], *pData);						
				}				
				pNext =  pNext->Next();
			}
		}		
		return true;
	}
	return false;	
}

void ColladaFile::LoadMaterials(XmlParser* pParser)
{
	pParser->FirstChildElement();

	while( pParser->HasElement()  )
	{
		if( (u32)pParser->GetValueAsInt() == s_uHash_material )
		{
			u32 uID = StringHash(pParser->GetAttribute("id"));

			//look for the insatnce of the effect for this material
			pParser->FirstChildElement();

			while( pParser->HasElement() )
			{
				if( (u32)pParser->GetValueAsInt() == s_uHash_instance_effect )
				{
					//found the name of the effect, that is all we need so get it and leave
					const char* szVal = pParser->GetAttribute("url");
					u32 uVal = StringHash(&szVal[1]);
					m_materialEffectMap.Add(uID, uVal);
					m_effectMaterialMap.Add(uVal, uID);	
					break;
				}
				pParser->NextSiblingElement();
			}

			pParser->Parent();
		}

		pParser->NextSiblingElement();
	}

	pParser->Parent();
}

void ColladaFile::LoadEffects(XmlParser* pParser)
{
	pParser->FirstChildElement();

	while(pParser->HasElement() )
	{
		if( (u32)pParser->GetValueAsInt() == s_uHash_effect )
		{
			u32 uVal = *m_effectMaterialMap[StringHash(pParser->GetAttribute("id"))];

			//found an effect that is in our map now get the texture file name
			if( uVal != 0 )
			{
				pParser->FirstChildElement();
				pParser->FirstChildElement();

				while( pParser->HasElement() )
				{
					pParser->FirstChildElement();

					if( (u32)pParser->GetValueAsInt() == s_uHash_surface && !strcmp(pParser->GetAttribute("type"),"2D") )
					{
						pParser->FirstChildElement();
						//found the texture
						const char* szVal = pParser->GetChildValue();
						m_textureMaterialMap.Add(StringHash(szVal), uVal);	
						pParser->Parent();
						pParser->Parent();
						break;
					}

					pParser->Parent();

					pParser->NextSiblingElement();
				}

				pParser->Parent();
				pParser->Parent();

			}

		}

		pParser->NextSiblingElement();
	}

	pParser->Parent();

}

void ColladaFile::LoadImages(XmlParser* pParser)
{
	pParser->FirstChildElement();

	while( pParser->HasElement() )
	{
		u32 uMat = *m_textureMaterialMap[StringHash(pParser->GetAttribute("id"))];

		//found the the texture for this effect, gets its file name and store it
		if( uMat != 0 )
		{
			pParser->FirstChildElement();

			const char* szFile = pParser->GetChildValue();			
			m_materialTextureMap.Add(uMat, szFile);

			pParser->Parent();
		}

		pParser->NextSiblingElement();
	}

	pParser->Parent();
}